#!/usr/bin/env python
# *-* encoding: utf8
# 
# Copyright (c) 2006 Stian Soiland
# 
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# 
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
#
# Author: Stian Soiland <stian@soiland.no>
# URL: http://soiland.no/i/src/
# License: LGPL
#
"""RDF store mixin.

Add RDF storage capabilities to the file system. 

Requires rdflib.
"""

from StringIO import StringIO

from rdflib.Graph import Graph
import rdflib

from uuid import UUID
import rdfstore

# Namespaces
ROOT=rdflib.Namespace("http://perfs.googlecode.com/")
FS=rdflib.Namespace(ROOT+"fs#")
# Dublin Core
DC=rdflib.Namespace("http://purl.org/dc/elements/1.1/")
DC_TERMS=rdflib.Namespace("http://purl.org/dc/terms/")


class RDFStore(object):
    def __init__(self, blockstore, uuid=None):
        self.blockstore = blockstore
        if uuid is None:
            uuid = UUID()
        self.uuid = uuid
        self.graph = Graph()
    
    def load(self):
        try:
            rdf = self.blockstore.retrieve(self.uuid.str)
        except rdfstore.NotFoundError:
            return
        self.graph.parse(StringIO(rdf), 
                         publicID=self.uuid.uri, 
                         format="xml")
   
    def save(self):
        self.graph.commit()
        rdf = StringIO()
        self.graph.serialize(rdf, format="xml", base=self.uuid.uri, encoding="utf8")
        if self.blockstore.exists(self.uuid.str):
            self.blockstore.remove(self.uuid.str)
        self.blockstore.store(rdf.getvalue(), self.uuid.str)

    def addN(self, *quads):
        self.graph.addN(*quads)
        self.save()
    
    def remove(self, subj, pred, obj):
        self.graph.remove((subj, pred, obj))  
        self.save()
    
    def parse(self, file, format):
        self.graph.parse(file, format=format)
        self.save()


    # IDEA
    
    # Named graphs, each graph is a transaction
    # The graph describes a directory, while 
    # each item in the directory is a directory
    # or a file (or something else). 
    # In addition the entries have metadata by
    # DC, such as creation date, author, etc.

    # Each transaction is also described by meta data,
    # like what are the previous transactions that it
    # superseedes, who made it where and when (DC), etc.
    
    # Each transaction/named graph can then be 
    # serialized and distributed just as the blocks.

    # The transaction metadata will help relate the 
    # transaction to other transactions, so you know which
    # transactions are (partly) no longer valid.
    
    # As such, the transactions will outline a graph of
    # revisions/versions of both the directories and
    # file content.

    def annotate(self, resource, predicate, object):
        uri = rdflib.URIRef(resource.identifier.uri)
        self.graph.add(uri, predicate, object)
    
    def annotations(self, resource):
        uri = rdflib.URIRef(resource.identifier.uri)
        for subj,pred,obj in self.graph.tuple((uri, None, None)):
            yield (pred, obj)
     
    def query(self, query, **bindings):
        variables = {}
        for variable,value in bindings.iteritems():
            if not variable.startswith("?"):
                variable = "?" + variable
            variables[variable] = value    
        return self.graph.query(query, initBindings=variables)    

